/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.neuralnetwork;

/**
 *
 * @author afspear
 */
public class Trainer {

    public static void trainOuputPerceptron(double desiredOutput, Perceptron outputPerceptron) {

        double output = outputPerceptron.output;
        //double error = output * (1 - output) * (desiredOutput - output);
        //new error fuction from http://www.doc.ic.ac.uk/~nd/surprise_96/journal/vol4/cs11/report.html#Appendix B - The back-propagation Algorithm - a mathematical approach
        double error = ((output - desiredOutput) * (output - desiredOutput)) / 2;
        outputPerceptron.error = error;
        //λ (Lambda) the Learning Rate: a real number constant, usually 0.2 for output layer neurons and 0.15 for hidden layer neurons.
        double λ = .2;
        double Δθ = λ * error;
        outputPerceptron.threshold += Δθ;
        for (Input input : outputPerceptron.inputs) {
            input.weight += Δθ * input.data;
        }
    }

    public static void trainHiddenLayerPerceptron(Perceptron hiddenLayerPerceptron) {
        double output = hiddenLayerPerceptron.output;
        double sumWeightsAndErrors = 0;
        for (Input input : hiddenLayerPerceptron.outputs) {
            sumWeightsAndErrors += input.toPerceptron.error * input.weight;
        }
        double error = output * (1 - output) * sumWeightsAndErrors;
        double λ = .15;
        double Δθ = λ * error;
        hiddenLayerPerceptron.threshold += Δθ;
        for (Input input : hiddenLayerPerceptron.inputs) {
            input.weight += Δθ * input.data;
        }
    }

    //this trains the network 
    public static void trainNetwork(double[] desiredOutputs, Network network) {
        int desiredOutputCounter = 0;
        for (Perceptron perceptron : network.outputLayer) {
            trainOuputPerceptron(desiredOutputs[desiredOutputCounter], perceptron);
            desiredOutputCounter++;
        }
        for (int i = network.matrix.size() - 2; i >= 0; i--) {
            for (Perceptron perceptron : network.matrix.get(i)) {
                trainHiddenLayerPerceptron(perceptron);
            }
        }
    }
}